/**
 * Licensed under the MIT License
 *
 * Copyright (c) 2010 Benoit Saint-Maxent
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package marcel.controller {
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	import marcel.controller.command.IAsyncCommand;
	import marcel.controller.command.ICommand;
	import marcel.debug.*;
	import marcel.utils.ClassUtils;


	/**
	 * FrontController listens to a particular instance of IEventDispatcher.
	 * Each time the received event type corresponds to a registered ICommand,
	 * the ICommand is triggered.
	 *
	 * @author Benoit Saint-Maxent
	 */
	public class FrontController {
		
		
		//--------------------------------------------------------------------------
		//
		//  Private vars
		//
		//--------------------------------------------------------------------------
		
		protected var _dispatcher : IEventDispatcher;
		protected var _d : Dictionary;
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------

		/**
		 * Constructs a new FrontController instance
		 * @param dispatcher IEventDispatcher which broadcasts events that would trigger ICommands
		 */
		public function FrontController(dispatcher : IEventDispatcher) {
			_dispatcher = dispatcher;
			_d = new Dictionary();
		}





		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 * Removes all registered Icommands
		 * and stops listening to all types.
		 */
		public function removeAll() : void {
			for (var type : String in _d) remove(type);
		}
		
		/**
		 * Stops listening to the passed-in event type
		 * and removes the registered-with ICommand
		 * @param type The event type for which we can unsubscribe the ICommand
		 */
		public function remove(type : String) : void {
			_dispatcher.removeEventListener(type, _handleEvent);
			var o : Object = _d[type];
			_d[type] = null;
			if(!o || o is Class) return;
			if(!(o is IAsyncCommand)) return;
			(o as IAsyncCommand).kill();
		}

		
		/**
		 * Registers a ICommand instance to be triggered each time the passed-in event type will be received
		 * @param	type	type of the event type
		 */
		public function addCommand(type : String, command : ICommand) : Boolean {
			if(isRegistered(type)) {
				debuge("A command is already registered with the type '" + type + "'.");
				return false;
			}
			if(!command) {
				debuge("Passed-in Command instance is invalid.");
				return false;
			}
			_d[type] = command;
			_dispatcher.addEventListener(type, _handleEvent);
			return true;
		}


		/**
		 * Registers a ICommand class to be triggered each time the passed-in event type will be received. A new instance will be created each time the event is recieved.
		 * @param	type	type of the event type
		 */
		public function addCommandClass(type : String, command : Class) : Boolean {
			if(isRegistered(type)) {
				debuge("A command is already registered with the type '" + type + "'.");
				return false;
			}
			if(!ClassUtils.isImplementationOf(command, ICommand)) {
				debuge("'" + command + "' should implement ICommand interface.");
				return false;
			}
			if(ClassUtils.isImplementationOf(command, IAsyncCommand)) {
				debugw("Async commaand added as Class (" + command + ") => a new instance will be created for each execute. If you need to receive the command RESULT event, please use the addCommand method");
			}
			_d[type] = command;
			_dispatcher.addEventListener(type, _handleEvent);
			return true;
		}

		
		/**
		 * If a command is registered with the passed-in event type, returns the registered ICommand
		 * @param type event type
		 * @return 	null if no ICommand is registered with the passed-in event type,
		 * 			the corresponding registered ICommand instance,
		 * 			or a new instance of the corresponding ICommand Class
		 */
		public function getCommand(type : String) : ICommand {
			var o : Object = _d[type];
			if(!o) return null;
			var c : ICommand;
			if(o is Class) c = new (o as Class)();
			else c = o as ICommand;
			return c;
		}


		/**
		 * Checks if a ICommand is registered with the passed-in event type.
		 * @param type event type
		 * @return true if a ICommand is registered with the passed-in event type, false if not
		 */
		public function isRegistered(type : String) : Boolean {
			return _d[type] != null;
		}

		/**
		 * Prepares the current FrontController to be removed from memory
		 */
		public function kill() : void {
			removeAll();
			_dispatcher = null;
		}

		
		//--------------------------------------------------------------------------
		//
		//  Private methods
		//
		//--------------------------------------------------------------------------
		protected function _handleEvent(event : Event) : void {
			var c : ICommand = getCommand(event.type);
			if(!c) return;
			c.execute(event);
		}
	}
}
